﻿#include "Sistema.h"

Sistema::Sistema(int maxPaises, int maxCiudades, int maxRecursos)
{
	this->maxPais=maxPaises;
	this->maxCiudades=maxCiudades;
	this->maxRecursos=maxRecursos;
	this->avlPaises=new AVLImp<Puntero<Pais>>();
	Puntero<FuncionHash<Cadena>> fh1=new CadenaFuncionHash(maxPaises+(maxPaises*2));
	Puntero<FuncionIguales<Cadena>> fIgualCadena=new FuncionIgualesCadena();
	this->hashPaises=new Hash<Cadena,Puntero<Pais>>(fh1,fIgualCadena,maxPaises+(maxPaises*2));
	Puntero<FuncionHash<Puntero<Recurso>>> fhRecur=new RecursoFuncionHash(maxRecursos+(maxRecursos*2));
	Puntero<FuncionIguales<Puntero<Recurso>>> fiRecur=new FuncionIgualesRecurso();
	this->hashRecOrdCron=new Hash<Puntero<Recurso>,Puntero<ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>>>(fhRecur,fiRecur,maxRecursos+(maxRecursos*2));
	this->mapa=new Mapa(maxCiudades*maxPaises);
}

Sistema::~Sistema()
{
	this->hashRecOrdCron->vacio();
}

//// Obligatorio 1
TipoRetorno Sistema::AltaPais(Cadena nombrePais)
{
	Puntero<Pais> p = new Pais(nombrePais,this->maxCiudades); 
	if(this->hashPaises->size()<this->maxPais){
		if(!this->hashPaises->estaDefinido(nombrePais)){
			this->avlPaises->agregar(p);
			this->hashPaises->operator [](nombrePais) = p;	
			return OK;
		}else{ 
			return ERROR;
		}
	}else{ 
		return ERROR;
	}
}

TipoRetorno Sistema::AltaCiudad(Cadena nombrePais, Cadena nombreCiudad, bool esCapital)
{
	Puntero<Ciudad> c=new Ciudad(nombreCiudad,esCapital,this->maxRecursos,nombrePais);
	if(this->hashPaises->estaDefinido(nombrePais))
	{	
		if(this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->size()<=this->maxCiudades){
			bool esta=this->hashPaises->operator [](nombrePais)->ObtenerCiudades()->esMiembro(c);
			if(!esta){
				this->hashPaises->operator [](nombrePais)->ObtenerCiudades()->agregar(c);
				this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->operator [](nombreCiudad)=c;
				this->mapa->agregarCiudad(c);
				if(esCapital)
				{
					if(this->hashPaises->operator [](nombrePais)->tieneCapital()==false)
					{
						this->hashPaises->operator [](nombrePais)->SetCapital(c);
					}
					else
					{
						return ERROR;
					}
				}
				return OK;
			}else{
				return ERROR;
			}
		}else{
			return ERROR;
		}
	}else{
		return ERROR;
	}
}

TipoRetorno Sistema::AltaRecurso(Cadena nombrePais, Cadena nombreCiudad, Cadena recurso, int puntaje, int cantidad, int valor)
{
	Puntero<Pais> p = new Pais(nombrePais,this->maxCiudades); 
	Puntero<Recurso> r=new Recurso(recurso,puntaje,cantidad,valor);

	if(puntaje>=0&&cantidad>=0&&valor>=0){
		if(this->hashPaises->estaDefinido(nombrePais))
		{
			if(this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->estaDefinido(nombreCiudad))
			{
				Puntero<Ciudad> c=this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->operator [](nombreCiudad);
				Puntero<ColaHeapImp<Puntero<Recurso>>> recursos = c->ObtenerRecurso();
				if(c->ObtenerRecurso()->largo()<this->maxRecursos){
					if(recursos->EstaDefinido(r)){
						//ver si ya existe ese recurso asi lo atualizo
						Puntero<Recurso> r1=recursos->devolverDato(r);
						r->SumarCantidad(r1->ObtenerCantidad());
						recursos->encolarConPrioridad(r,puntaje);
						return OK;
					}else{
						Puntero<ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>> l;
						recursos->encolarConPrioridad(r,r->ObtenerPuntaje());
						Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>> t= new Tupla<Puntero<Pais>,Puntero<Ciudad>>(p,c);
						if(!this->hashRecOrdCron->estaDefinido(r))
						{
							l = new ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>();
							//Para cada recurso nuevo ago un puntero a nuevo a lista
							l->AgregarFinal(t);
							this->hashRecOrdCron->operator [](r)=l;
						}else{

							l=this->hashRecOrdCron->operator [](r);
							l->AgregarFinal(t);
						}

						return OK;
					}
				}else{
					return ERROR;
				}
			}else{
				return ERROR;
			}

		}else{
			return ERROR;
		}
	}else{
		return ERROR;
	}
}

Tupla<TipoRetorno, Puntero<Iterador<pPais>>> Sistema::ImprimirJuego() const
{
	if(this->avlPaises->esVacio()==false){
		this->avlPaises->CrearIterador();//lleno lista iter
		Puntero<Iterador<Puntero<Pais>>> it= this->avlPaises->ObtenerIterador();
		Puntero<Iterador<pPais>> it2=  new CastIterador<Pais,IPais>(it);
		return Tupla<TipoRetorno, Puntero<Iterador<pPais>>>(OK,it2);
	}else{
		return Tupla<TipoRetorno, Puntero<Iterador<pPais>>>(ERROR,NULL);
	}
}

Tupla<TipoRetorno, pRecurso> Sistema::RecursoMasValioso(Cadena nombrePais, Cadena nombreCiudad)
{
	//if(this->hashPaises->estaDefinido(nombrePais))
	//{
	//	if(this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->estaDefinido(nombreCiudad))
	//	{
	//		//validar si no existe la ciudad y si no existe el pais
	//		Puntero<Ciudad> c= this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->operator [](nombreCiudad);
	//		if(c->ObtenerRecurso()->esVacia()==false){
	//			Puntero<Recurso> r;
	//			c->ObtenerRecurso()->desencolar(r);
	//			if(r!=NULL){
	//				Puntero<IRecurso> iR=r.Cast<IRecurso>();
	//				return Tupla<TipoRetorno, pRecurso>(OK, iR);
	//			}else{
	//				return ERROR;
	//			}
	//		}else{
	//			return ERROR;
	//		}

	//	}else{
	//		return ERROR;
	//	}
	//}else{
	//	return ERROR;
	//}
	return Tupla<TipoRetorno, pRecurso>(NO_IMPLEMENTADA, new Recurso("asd",0,0,0));
}

Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais, pCiudad>>>> Sistema::CiudadesRecurso(Cadena recurso) const
{
	Puntero<ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>> l = new ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>();
	Puntero<Recurso> r=new Recurso(recurso,0,0,0);
	if(this->hashRecOrdCron->estaDefinido(r)){
		l=this->hashRecOrdCron->operator [](r);
		Puntero<Iterador<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>> it=new IteradorListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>(l);		
		Puntero<ListaImp<Tupla<pPais,pCiudad>>> lista= new ListaImp<Tupla<pPais,pCiudad>> ();
		while(it->HayElemento())
		{
			Puntero<IPais> iP=it->ElementoActual()->GetDato1().Cast<IPais>();
			Puntero<ICiudad>iC=it->ElementoActual()->GetDato2().Cast<ICiudad>();
			Tupla<Puntero<IPais>,Puntero<ICiudad>> t=Tupla<Puntero<IPais>,Puntero<ICiudad>>(iP,iC);
			lista->Agregar(t);
			it->Avanzar();
		}
		Puntero<Iterador<Tupla<pPais, pCiudad>>> it2= new IteradorListaImp<Tupla<pPais,pCiudad>>(lista);
		return Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais, pCiudad>>>>(OK,it2);
	}else{
		return ERROR;
	}
}

//// Tipo 1
TipoRetorno Sistema::AltaCamino(Cadena nombrePaisO, Cadena nombreCiudadO, Cadena nombrePaisD, Cadena nombreCiudadD, int distancia, int costo)
{
	if(this->hashPaises->estaDefinido(nombrePaisO)&&this->hashPaises->estaDefinido(nombrePaisD))
	{
		Puntero<Pais> pOrigen=this->hashPaises->operator [](nombrePaisO);
		Puntero<Pais> pDestino=this->hashPaises->operator [](nombrePaisD);
		if(pOrigen->ObtenerHashCiudades()->estaDefinido(nombreCiudadO)&& pDestino->ObtenerHashCiudades()->estaDefinido(nombreCiudadD))
		{
			Puntero<Ciudad> cOrigen=pOrigen->ObtenerHashCiudades()->operator [](nombreCiudadO);
			Puntero<Ciudad> cDestino=pDestino->ObtenerHashCiudades()->operator [](nombreCiudadD);
			this->mapa->agregarCamino(cOrigen,cDestino,costo,distancia);

			return OK;
		}else{
			return ERROR;
		}
	}else{
		return ERROR;
	}
}

TipoRetorno Sistema::ListadoMapa(Cadena nombrePais, Cadena nombreCiudad) const
{
	if(this->hashPaises->estaDefinido(nombrePais))
	{
		Puntero<Pais> pais=this->hashPaises->operator [](nombrePais);
		if(pais->ObtenerHashCiudades()->estaDefinido(nombreCiudad))
		{
			if(this->mapa->cantidadVertices()!=1){
				Puntero<Ciudad> c=pais->ObtenerHashCiudades()->operator [](nombreCiudad);
				this->mapa->ListarMapa(c);
				return OK;
			}else
			{
				cout<<"No hay rutas en el sistema"<<endl;
				return ERROR;
			}
		}else{

			return ERROR;
		}	
	}else{
		return ERROR;
	}
}

TipoRetorno Sistema::Importar2(Cadena nombrePais, Cadena nombreCiudad, Cadena nombreRecurso, int cantidad)
{	
	Puntero<Recurso> rAComprar=new Recurso(nombreRecurso,0,0,0);
	if(this->hashPaises->estaDefinido(nombrePais))
	{
		Puntero<Pais> pais=this->hashPaises->operator [](nombrePais);
		if(pais->ObtenerHashCiudades()->estaDefinido(nombreCiudad))
		{
		//agregar a ciudad su pais!!!!!!!!!!!!!!!!!!!!!!!!!!1
			Puntero<Ciudad> c=pais->ObtenerHashCiudades()->operator [](nombreCiudad);
			Puntero<Tupla<Puntero<Ciudad>,Puntero<Recurso>>> t= this->mapa->Importar(c,rAComprar,cantidad);
			Puntero<Recurso> rQueSeVende=t->GetDato2();
			Puntero<Ciudad> cQueVende=t->GetDato1();
			Puntero<Recurso> rMenorPrecio=cQueVende->ObtenerRecurso()->devolverDato(rQueSeVende);
			if(c->ObtenerRecurso()->EstaDefinido(rAComprar)){
				//ver si ya existe ese recurso asi lo atualizo
				Puntero<Recurso> r1=c->ObtenerRecurso()->devolverDato(rAComprar);
				if(r1->ObtenerValor()<rMenorPrecio->ObtenerValor()){
					r1->SetValor(rMenorPrecio->ObtenerValor());
				}
				r1->SetCantidad(r1->ObtenerCantidad()+cantidad);
				rMenorPrecio->SetCantidad(rMenorPrecio->ObtenerCantidad()-cantidad);
				cout<<"Se compro "<<cantidad <<"unidades de "<<rMenorPrecio->ObtenerNombre()<<" a "<<cQueVende->ObtenerPais()<<"-"<<cQueVende->ObtenerNombre()<<endl;
				cout<<"Saldos "<<cQueVende->ObtenerPais()<<"-"<<cQueVende->ObtenerNombre()<<":"<<rMenorPrecio->ObtenerCantidad()<<endl;
				cout<<"       "<<nombrePais<<"-"<<nombreCiudad<<":"<<cantidad+r1->ObtenerCantidad()<<endl;

				return OK;



			}else{
				AltaRecurso(nombrePais,nombreCiudad,rMenorPrecio->ObtenerNombre(),rMenorPrecio->ObtenerPuntaje(),cantidad,rMenorPrecio->ObtenerValor());
				rMenorPrecio->SetCantidad(rMenorPrecio->ObtenerCantidad()-cantidad);
				cout<<"Se compro "<<cantidad <<"unidades de "<<rMenorPrecio->ObtenerNombre()<<" a "<<cQueVende->ObtenerPais()<<"-"<<cQueVende->ObtenerNombre()<<endl;
				cout<<"Saldos "<<cQueVende->ObtenerPais()<<"-"<<cQueVende->ObtenerNombre()<<":"<<rMenorPrecio->ObtenerCantidad()<<endl;
				cout<<"       "<<nombrePais<<"-"<<nombreCiudad<<":"<<cantidad<<endl;
				return OK;
			}
		}else{
		
			return ERROR;
		}	
	}else{
		 return ERROR;
	}
}

Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais,pCiudad>>>> Sistema::RutaMasRapida(Cadena nombrePaisO, Cadena nombreCiudadO, Cadena nombrePaisD, Cadena nombreCiudadD) const
{
	if(this->hashPaises->estaDefinido(nombrePaisO)&&this->hashPaises->estaDefinido(nombrePaisD))
	{
		Puntero<Pais> pOrigen=this->hashPaises->operator [](nombrePaisO);
		Puntero<Pais> pDestino=this->hashPaises->operator [](nombrePaisD);
		if(pOrigen->ObtenerHashCiudades()->estaDefinido(nombreCiudadO)&& pDestino->ObtenerHashCiudades()->estaDefinido(nombreCiudadD))
		{
			Puntero<Ciudad> ciudadOrigen=pOrigen->ObtenerHashCiudades()->operator [](nombreCiudadO);
			Puntero<Ciudad> ciudadDestino=pDestino->ObtenerHashCiudades()->operator [](nombreCiudadD);
			cout<<"Camino más corto entre: "<<ciudadOrigen->ObtenerNombre()<<" y "<< ciudadDestino<<endl;
			Puntero<Iterador<Puntero<Ciudad>>> it=this->mapa->CaminoMasCorto(ciudadOrigen,ciudadDestino);
			int cont =1;
			Puntero<ListaImp<Tupla<pPais,pCiudad>>> ruta=new ListaImp<Tupla<pPais,pCiudad>>();
			while(it->HayElemento())
			{
				cout<<it->ElementoActual()->ObtenerNombre()<<cont<<" - ";
				Puntero<Pais> p=this->hashPaises->operator [](it->ElementoActual()->ObtenerPais());
				Tupla<pPais,pCiudad> t= Tupla<pPais,pCiudad>(p.Cast<IPais>(),it->ElementoActual().Cast<ICiudad>());
				ruta->AgregarFinal(t);
				it->Avanzar();
				cont++;
			}
				cout<<" "<<endl;
				return Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais,pCiudad>>>>(OK,new IteradorListaImp<Tupla<pPais,pCiudad>>(ruta));
		}else{
			return Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais,pCiudad>>>>(ERROR,NULL);
		}
	}else{

		return Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais,pCiudad>>>>(ERROR,NULL);
	}
}

Tupla<TipoRetorno, Puntero<Iterador<pRecurso>>> Sistema::SeleccionRecurso(Cadena nombrePais, Cadena nombreCiudad, int k) const
{
	Puntero<ListaImp<pRecurso>> lista= new ListaImp<pRecurso>();
	if(this->hashPaises->estaDefinido(nombrePais))
	{
		Puntero<Pais> p=this->hashPaises->operator [](nombrePais);
		if(p->ObtenerHashCiudades()->estaDefinido(nombreCiudad))
		{
			Puntero<Ciudad> c=p->ObtenerHashCiudades()->operator [](nombreCiudad);
			Puntero<Iterador<Puntero<Recurso>>> it=this->mapa->SeleccionarRec(c,k);
			
			while(it->HayElemento())
			{
				cout<<it->ElementoActual()->ObtenerNombre()<<" "<<endl;
				lista->AgregarFinal(it->ElementoActual().Cast<IRecurso>());
				it->Avanzar();
			}

			return Tupla<TipoRetorno, Puntero<Iterador<pRecurso>>>(OK, new IteradorListaImp<pRecurso>(lista));
		}else
		{
			return Tupla<TipoRetorno, Puntero<Iterador<pRecurso>>>(ERROR, NULL);
		}
	}else
	{
		return Tupla<TipoRetorno, Puntero<Iterador<pRecurso>>>(ERROR,NULL);
	}
}

//// Tipo 2
Tupla<TipoRetorno, bool> Sistema::Conectividad() const
{
	if(this->mapa->cantidadVertices()!=1)
	{
		bool conect=this->mapa->Conectividad();
		if(conect)
		{
			cout<<"Es Conexo"<<endl;
		}else
		{
			cout<<"No es Conexo"<<endl;
		}
		
		Tupla<TipoRetorno,bool> t=Tupla<TipoRetorno,bool>(OK,conect);
		
		return t;
	}else
	{
		return Tupla<TipoRetorno,bool>(ERROR,false);
	
	}
}

TipoRetorno Sistema::ConexionBarata(Puntero<Iterador<Tupla<pPais,pCiudad>>> ciudades) const
{
	if(this->mapa->cantidadVertices()!=1)
	{
		this->mapa->ConexionBarata(ciudades);
		return OK;
	}else
	{
		return ERROR;
	}
}

Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais,pCiudad>>>> Sistema::Recolectar(Cadena nombrePais, Cadena nombreCiudad, Cadena nombreRecurso, int k) const
{
	Puntero<Recurso> r=new Recurso(nombreRecurso,0,0,0);
	Puntero<ListaImp<Tupla<pPais,pCiudad>>> listaADev=new ListaImp<Tupla<pPais,pCiudad>>();
	if(this->hashPaises->estaDefinido(nombrePais)&& this->hashRecOrdCron->estaDefinido(r))
	{
		Puntero<Pais> p=this->hashPaises->operator [](nombrePais);
		if(p->ObtenerHashCiudades()->estaDefinido(nombreCiudad))
		{
			Puntero<Ciudad> c=p->ObtenerHashCiudades()->operator [](nombreCiudad);
			Puntero<ListaImp<Puntero<Ciudad>>> lista=this->mapa->Recolectar(c,nombreRecurso,k);
			Puntero<Iterador<Puntero<Ciudad>>> it=lista->ObtenerIterador();
			while(it->HayElemento())
			{
				Puntero<Pais> p=this->hashPaises->operator [](it->ElementoActual()->ObtenerPais());
				Tupla<pPais,pCiudad> t= Tupla<pPais,pCiudad>(p.Cast<IPais>(),it->ElementoActual().Cast<ICiudad>());
				listaADev->AgregarFinal(t);
				it->Avanzar();
			}
			return Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais,pCiudad>>>>(OK, new IteradorListaImp<Tupla<pPais,pCiudad>>(listaADev));
		}else
		{
			return Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais,pCiudad>>>>(ERROR, NULL);
		}
	}else	
	{
	return Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais,pCiudad>>>>(ERROR, NULL);
	}

	
}

Tupla<TipoRetorno, Puntero<Iterador<Puntero<Iterador<Tupla<pPais,pCiudad>>>>>> Sistema::RutasEntre2Paises(Cadena nombrePaisO, Cadena nombrePaisD) const
{
	if(this->hashPaises->estaDefinido(nombrePaisO) && this->hashPaises->estaDefinido( nombrePaisD))
	{
		Puntero<Pais> p=this->hashPaises->operator [](nombrePaisO);
		Puntero<ListaImp<Puntero<ListaImp<Puntero<Ciudad>>>>> sol=this->mapa->RutasEntrePaises(p->ObtenerCapital().Cast<Ciudad>(),nombrePaisD);
		Puntero<Iterador<Puntero<ListaImp<Puntero<Ciudad>>>>> itTodosCaminos=new IteradorListaImp<Puntero<ListaImp<Puntero<Ciudad>>>>(sol);
		Puntero<ListaImp<Puntero<Iterador<Tupla<pPais,pCiudad>>>>> listaDeIteradores=new ListaImp<Puntero<Iterador<Tupla<pPais,pCiudad>>>>();
		while(itTodosCaminos->HayElemento())
		{
			Puntero<Iterador<Puntero<Ciudad>>> itCamino=new IteradorListaImp<Puntero<Ciudad>>(itTodosCaminos->ElementoActual());
			Puntero<ListaImp<Tupla<pPais,pCiudad>>> listaADev=new ListaImp<Tupla<pPais,pCiudad>>();
			while(itCamino->HayElemento())
			{
				Puntero<Pais> p=this->hashPaises->operator [](itCamino->ElementoActual()->ObtenerPais());
				Tupla<pPais,pCiudad> t= Tupla<pPais,pCiudad>(p.Cast<IPais>(),itCamino->ElementoActual().Cast<ICiudad>());
				listaADev->AgregarFinal(t);
				itCamino->Avanzar();
			}
			Puntero<Iterador<Tupla<pPais,pCiudad>>> j=new IteradorListaImp<Tupla<pPais,pCiudad>>(listaADev);
			listaDeIteradores->AgregarFinal(j);
			itTodosCaminos->Avanzar();
		}
		return Tupla<TipoRetorno, Puntero<Iterador<Puntero<Iterador<Tupla<pPais,pCiudad>>>>>>(OK, new IteradorListaImp<Puntero<Iterador<Tupla<pPais,pCiudad>>>>(listaDeIteradores));
	}
	return Tupla<TipoRetorno, Puntero<Iterador<Puntero<Iterador<Tupla<pPais,pCiudad>>>>>>(ERROR, NULL);
}

//// Opcionales
Tupla<TipoRetorno, int> Sistema::ValorMapa() const
{
	int valor=0;
	Tupla<int,Puntero<Hash<Puntero<Pais>, int>>> tuplaValorMapa=this->mapa->ValorMapa();
	Puntero<Hash<Puntero<Pais>, int>> paisesConectados=tuplaValorMapa.GetDato2();
	this->avlPaises->CrearIterador();
	Puntero<Iterador<Puntero<Pais>>> it=this->avlPaises->ObtenerIterador();
	while(it->HayElemento())
	{
		Puntero<Pais> p=it->ElementoActual();
		if(paisesConectados->estaDefinido(p))
		{
			valor=valor+it->ElementoActual()->CalcularRiqueza();
			
		}
		it->Avanzar();
	}
	cout<<valor<<"-"<<tuplaValorMapa.GetDato1()<<"="<<valor-tuplaValorMapa.GetDato1()<<endl;
	return Tupla<TipoRetorno, int>(OK, valor-tuplaValorMapa.GetDato1());
}
